package com.expert.service.Impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.expert.Mapper.ExtractResultMapper;
import com.expert.context.BaseContext;
import com.expert.dto.SupplementaryDTO;
import com.expert.enums.ProjectStatus;
import com.expert.exception.AddException;
import com.expert.exception.BaseException;
import com.expert.exception.UpdateException;
import com.expert.pojo.*;
import com.expert.service.*;
import com.expert.vo.ExtractResultCheckVo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName: ExtractResultServiceImpl
 * @Author: cxs
 * @Date: 2024/03/26 15:39
 * @Description:
 **/
@Service
public class ExtractResultServiceImpl extends ServiceImpl<ExtractResultMapper, ExtractResult> implements ExtractResultService {

    @Autowired
    ExtractResultMapper extractResultMapper;
    @Autowired
    ProjectService projectService;
    @Autowired
    RabbitSendService service;
    @Autowired
    ExtractService extractService;
    @Autowired
    RabbitSendService rabbitSendService;
    @Autowired
    CategoryService categoryService;
    @Autowired
    ExpertService expertService;
    @Autowired
    MailService mailService;
    @Autowired
    ExpertScoreService expertScoreService;
    /**
     * 查询一年超过十次的情况
     *
     * @param expertList
     * @return
     */
    @Override
    public List<Long> queryExtractedOver(List<Long> expertList) {

        LocalDateTime begin = LocalDateTime.of(LocalDate.from(LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear())), LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(LocalDate.from(LocalDateTime.now().with(TemporalAdjusters.lastDayOfYear())), LocalTime.MAX);
        System.out.println(expertList);
        List<Long> overList=this.extractResultMapper.queryOver(begin,end);
        if (overList.size()>0) {
            expertList.removeAll(overList);
        }

        return expertList;
    }

    /**
     * 验证抽取结果
     *
     * @param id
     * @return
     */
    @Override
    public List<ExtractResultCheckVo> getExtractResultCheck(Long id) {
        List<ExtractResult> extractResultList = this.lambdaQuery()
                .eq(ExtractResult::getProjectId, id)
//                .gt(ExtractResult::getCreateTime,LocalDateTime.now().plusDays(-1))
                .eq(ExtractResult::getObsolete,0)
//                .or()
//                .eq(ExtractResult::getAcceptFlag,1)
//                .isNotNull(ExtractResult::getAcceptTime)
//                .eq(ExtractResult::getObsolete,0)
                .list();
        return extractResultList.stream().map(item -> {
            ExtractResultCheckVo extractResultCheckVo = new ExtractResultCheckVo();
            extractResultCheckVo.setAcceptFlag(item.getAcceptFlag());
            extractResultCheckVo.setAttendanceFlag(item.getAttendanceFlag());
            Expert expert = this.expertService.getById(item.getExpertId());
            if (expert!=null){
                extractResultCheckVo.setExpertId(expert.getId());
                extractResultCheckVo.setExpertName(expert.getExpertName());
                extractResultCheckVo.setExpertTitle(expert.getExpertTitle());
                Category category = this.categoryService.getById(expert.getExpertCategory());
                extractResultCheckVo.setExpertCategory(category.getId());
                extractResultCheckVo.setCategoryName(category.getCategoryCode() + " " + category.getCategoryName());
            }
            return extractResultCheckVo;
        }).toList();
    }

    /**
     * 重新抽取专家信息
     *
     * @param id 项目id
     */
    @Override
    public void reExtract(Long id) {
        //获取项目信息
        Project project = this.projectService.getById(id);
        if (project.getProjectReviewTime().plusDays(-2).isBefore(LocalDateTime.now())){
            throw  new UpdateException("项目评审时间距今不足2天，重新抽取失败");
        }
        if (project.getProjectStatus()> ProjectStatus.NOT_REVIEW.getCode()){
            throw new UpdateException("项目已评审，重新抽取失败");
        }
        //将原来的项目抽取结果置为失效
        List<ExtractResult> results = this.lambdaQuery()
                .eq(ExtractResult::getProjectId, id)
                .eq(ExtractResult::getObsolete,0)
                .list();
        results.forEach(result->{
            result.setObsolete(1);
            this.updateById(result);
            //通知专家
           rabbitSendService.sendReExtractMessage(result.getExpertId(),id);
        });
        //重新抽取
        this.extractService.extractExpert(id);
    }

    /**
     * 取消抽取专家信息
     *
     * @param id 项目id
     */
    @Override
    public void cancelExtractResult(Long id) {
        Project project = this.projectService.getById(id);
        if (project.getProjectReviewTime().plusDays(-2).isBefore(LocalDateTime.now())){
            throw  new UpdateException("距离项目评审时间不足两天，取消抽取专家信息失败");
        }
        List<ExtractResult> extractResultList = this.lambdaQuery()
                .eq(ExtractResult::getProjectId, id)
                .eq(ExtractResult::getObsolete,0)
                .list();
        //删除并通知
        extractResultList.forEach(result->{
            result.setObsolete(1);
            this.updateById(result);
            //通知专家
            rabbitSendService.sendCancelExtractResult(result.getExpertId(),id);
        });
        project.setProjectStatus(ProjectStatus.NOT_DRAWN.getCode());
        this.projectService.updateById(project);

    }

    /**
     * 补抽专家信息
     *
     * @param id 项目id信息
     */
    @Override
    public boolean checkIsSupplementary(Long id) {
        Project project = this.projectService.getById(id);
        Integer resultCount = Math.toIntExact(this.lambdaQuery().eq(ExtractResult::getObsolete, 1).count());
        if (resultCount.equals(project.getProjectReviewNumber())){
            throw new AddException("当前抽取专家人数已满足抽取人数");
        }
        return true;
    }

    /**
     * 补抽专家
     *
     * @param supplementaryDTO
     */
    @Override
    @Transactional
    public void supplementary(SupplementaryDTO supplementaryDTO) {
        Project project = this.projectService.getById(supplementaryDTO.getProjectId());
        Set<Long> extracted=new HashSet<>();
       List<Long> avoidList  = new ArrayList<>(this.lambdaQuery()
               .eq(ExtractResult::getProjectId, supplementaryDTO.getProjectId())
               .eq(ExtractResult::getObsolete,0)
               .list()
               .stream()
               .map(ExtractResult::getExpertId).toList());
      supplementaryDTO.getExtractDefaultExpert().forEach(defaultExpert->{
          if (avoidList.contains(defaultExpert)){
              throw new AddException("默认抽取专家中已存在已抽取专家");
          }
        });
        if (supplementaryDTO.getExtractDefaultExpert()==null||supplementaryDTO.getOthers().size()==0){
            throw new AddException("抽取条件不能为空");
        }
        List<Long> extractAvoidDefaultExpert = supplementaryDTO.getExtractAvoidDefaultExpert();
        if (extractAvoidDefaultExpert!=null&&extractAvoidDefaultExpert.size()>0){
            avoidList.addAll(extractAvoidDefaultExpert);
        }
        List<String> avoidUnit=new ArrayList<>();
        List<String> avoidRegion = new ArrayList<>();
        if (supplementaryDTO.getAvoidUnit()!=null) {
          avoidUnit = Arrays.stream(supplementaryDTO.getAvoidUnit().split("；")).toList();
        }
        if (supplementaryDTO.getAvoidRegion()!=null) {
             avoidRegion = Arrays.stream(supplementaryDTO.getAvoidRegion().split("；")).toList();
        }
        //封装抽取条件
        List<ExtractCondition> conditions = new ArrayList<>(supplementaryDTO.getExtractDefaultExpert().stream().map(de -> {
            ExtractCondition condition = new ExtractCondition();
            condition.setExtractDefaultExpert(de);
            return condition;
        }).toList());
        AtomicInteger num=new AtomicInteger();
        num.addAndGet(conditions.size());
        supplementaryDTO.getOthers().forEach(other->{
            ExtractCondition extractCondition=new ExtractCondition();
            if (other.getExtractNumber()>0&&(other.getExtractCategory()!=null||other.getExpertTitle()!=null)){
                num.getAndAdd(other.getExtractNumber());
                extractCondition.setExpertTitle(other.getExpertTitle());
                extractCondition.setExtractNumber(other.getExtractNumber());
                Long extractCategory = other.getExtractCategory();
                if (extractCategory!=null){
                    Category category = this.categoryService.getById(extractCategory);
                    extractCondition.setExtractCategory(extractCategory);
                    extractCondition.setExtractCategoryName(category.getCategoryCode()+" "+category.getCategoryName());
                }
                conditions.add(extractCondition);
                extractCondition.setProjectId(supplementaryDTO.getProjectId());
            }
        });
        if (num.get()>project.getProjectReviewNumber()-this.lambdaQuery()
                .eq(ExtractResult::getProjectId, supplementaryDTO.getProjectId())
                .eq(ExtractResult::getObsolete,0)
                .list()
                .stream()
                .map(ExtractResult::getExpertId).count()){
            throw  new AddException("抽取人数超过项目抽取人数");
        }
        List<String> finalAvoidUnit = avoidUnit;
        List<String> finalAvoidRegion = avoidRegion;
        conditions.forEach(condition -> {
            if (condition.getExtractDefaultExpert()!=null){
                extracted.add(condition.getExtractDefaultExpert());
            }else {
                List<Long> expertList = this.expertService.lambdaQuery()
                        .notIn(avoidList.size() > 0, Expert::getId, avoidList)
                        .eq(Expert::getDeleted,0)
                        .and(finalAvoidUnit.size()>0|| finalAvoidRegion.size()>0, wrapper -> {
                            finalAvoidUnit.forEach(unit -> {
                                wrapper.notLike(Expert::getExpertUnit,unit);
                            });
                            finalAvoidRegion.forEach(region->{
                                wrapper.notLike(Expert::getExpertRegion,region);
                            });
                        })
                        .eq(condition.getExtractCategory()!=null,Expert::getExpertCategory,condition.getExtractCategory())
                        .eq(StringUtils.isNotEmpty(condition.getExpertTitle()),Expert::getExpertTitle,condition.getExpertTitle())
                        .notIn(extracted.size()>0,Expert::getId,extracted)
                        .list().stream().map(Expert::getId).toList();

                //删除今年抽取超过10次的情况
                List<Long> notOverList=new ArrayList<>(this.queryExtractedOver(expertList));
                if (notOverList.size()<condition.getExtractNumber()){
                    throw  new AddException("符合条件专家人数不满足抽取条件");
                }
                Collections.shuffle(notOverList);
                for (int i = 0; i < condition.getExtractNumber(); i++) {
                    extracted.add(notOverList.get(i));
                }
            }
        });
        String attendanceCode = RandomStringUtils.randomAlphanumeric(6);
        List<ExtractResult> extractResults = extracted.stream().map(expert -> {
            ExtractResult result = ExtractResult.builder()
                    .expertId(expert)
                    .projectId(supplementaryDTO.getProjectId())
                    .attendanceCode(attendanceCode)
                    .createUser(BaseContext.getCurrentId())
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .updateUser(BaseContext.getCurrentId())
                    .build();
            service.send(expert,supplementaryDTO.getProjectId());
            return result;
        }).toList();
        this.saveBatch(extractResults);
    }

    /**
     * 催促专家
     *
     * @param project
     * @param id
     */
    @Override
    public void urged(Long project, Long id) {
        Project projectInfo = this.projectService.getById(project);
        if (projectInfo.getProjectReviewTime().plusHours(-1).isAfter(LocalDateTime.now())){
            throw  new BaseException("当前未到评审时间，请耐心等待");
        }
        Expert expert = this.expertService.getById(id);
        this.mailService.sendTextMailMessage(expert.getExpertEmail(),"评审迟到提醒",
                projectInfo.getProjectName()+"评审即将开始，请尽快到达评审地点，否则将无法签到");
    }

    /**
     * 评价专家
     *
     */
    @Override
    public void scoreExpert(ExpertScore expertScore) {
        Long count = this.expertScoreService.lambdaQuery()
                .eq(ExpertScore::getExpertId, expertScore.getExpertId())
                .eq(ExpertScore::getProjectId, expertScore.getProjectId()).count();
        if (count>0){
            throw  new AddException("本次已打分，请勿重复打分");
        }
       this.expertScoreService.save(expertScore);
        List<ExpertScore> allList = this.expertScoreService.lambdaQuery().eq(ExpertScore::getExpertId, expertScore.getExpertId()).list();
       if (!allList.isEmpty()){
           AtomicReference<Double> total= new AtomicReference<>((double) 0);
           allList.forEach(item->{
              total.getAndUpdate(v-> v +item.getScore()) ;
           });
           Expert expert = this.expertService.getById(expertScore.getExpertId());
           expert.setExpertScore(total.get()/allList.size());
           this.expertService.updateById(expert);
       }

    }
}
